{
    "cells": [
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "<i>Copyright (c) Recommenders contributors.</i>\n",
                "\n",
                "<i>Licensed under the MIT License.</i>"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "# Quickstart to integrate Recommenders in AzureML Designer\n",
                "\n",
                "This notebook shows how to integrate any algorithm in Recommenders library into AzureML Designer. \n",
                "\n",
                "[AzureML Designer](https://docs.microsoft.com/en-us/azure/machine-learning/concept-designer) lets you visually connect datasets and modules on an interactive canvas to create machine learning models. \n",
                "\n",
                "One of the features of AzureML Designer is that it is possible for developers to integrate any python library to make it available as a module/component. In this notebook are are going to show how to integrate [SAR](sar_movielens.ipynb) and several other modules in Designer.\n",
                "\n",
                "**Note that custom module is renamed to component.**\n",
                "\n",
                "## Component implementation\n",
                "\n",
                "The scenario that we are going to reproduce in Designer, as a reference example, is the content of the [SAR quickstart notebook](sar_movielens.ipynb). In it, we load a dataset, split it into train and test sets, train SAR algorithm, predict using the test set and compute several ranking metrics (precision at k, recall at k, MAP and nDCG).\n",
                "\n",
                "For the pipeline that we want to create in Designer, we need to build the following modules:\n",
                "\n",
                "- Stratified splitter\n",
                "- SAR training\n",
                "- SAR prediction\n",
                "- Precision at k\n",
                "- Recall at k\n",
                "- MAP\n",
                "- nDCG\n",
                "\n",
                "The python code is defined with a python entry and a yaml file. All the python entries and yaml files for this pipeline can be found in [contrib/azureml_designer_modules](../../contrib/azureml_designer_modules).\n",
                "\n",
                "\n",
                "### Define python entry\n",
                "\n",
                "To illustrate how a python entry is defined we are going to explain the [precision at k entry](../../contrib/azureml_designer_modules/entries/precision_at_k_entry.py). A simplified version of the code is shown next:\n",
                "\n",
                "```python\n",
                "# Dependencies\n",
                "from azureml.studio.core.data_frame_schema import DataFrameSchema\n",
                "from azureml.studio.core.io.data_frame_directory import (\n",
                "    load_data_frame_from_directory,\n",
                "    save_data_frame_to_directory,\n",
                ")\n",
                "from recommenders.evaluation.python_evaluation import precision_at_k\n",
                "\n",
                "# First, the input variables of precision_at_k are defined as argparse arguments\n",
                "if __name__ == \"__main__\":\n",
                "    parser = argparse.ArgumentParser()\n",
                "    parser.add_argument(\"--rating-true\", help=\"True DataFrame.\")\n",
                "    parser.add_argument(\"--rating-pred\", help=\"Predicted DataFrame.\")\n",
                "    parser.add_argument(\n",
                "        \"--col-user\", type=str, help=\"A string parameter with column name for user.\"\n",
                "    )\n",
                "    # ... more arguments\n",
                "    args, _ = parser.parse_known_args()\n",
                "\n",
                "    # This module has two main inputs from the canvas, the true and predicted labels\n",
                "    # they are loaded into the runtime as a pandas DataFrame\n",
                "    rating_true = load_data_frame_from_directory(args.rating_true).data\n",
                "    rating_pred = load_data_frame_from_directory(args.rating_pred).data\n",
                "\n",
                "    # The python function is instantiated and the computation is performed\n",
                "    eval_precision = precision_at_k(rating_true, rating_pred)\n",
                "    \n",
                "    # To output the result to Designer, we write it as a DataFrame\n",
                "    score_result = pd.DataFrame({\"precision_at_k\": [eval_precision]})\n",
                "    save_data_frame_to_directory(\n",
                "        args.score_result,\n",
                "        score_result,\n",
                "        schema=DataFrameSchema.data_frame_to_dict(score_result),\n",
                "    )\n",
                "```\n",
                "\n",
                "\n",
                "### Define component specification yaml\n",
                "\n",
                "Once we have the python entry, we need to create the yaml file that will interact with Designer, [precision_at_k.yaml](../../recommenders/azureml/azureml_designer_modules/module_specs/precision_at_k.yaml).\n",
                "\n",
                "```yaml\n",
                "$schema: http://azureml/sdk-2-0/CommandComponent.json\n",
                "name: microsoft.com.cat.precision_at_k\n",
                "version: 1.1.1\n",
                "display_name: Precision at K\n",
                "type: CommandComponent\n",
                "description: 'Precision at K metric from Recommenders repo: https://github.com/Microsoft/Recommenders.'\n",
                "tags:\n",
                "  Recommenders:\n",
                "  Metrics:\n",
                "inputs:\n",
                "  rating_true:\n",
                "    type: AnyDirectory\n",
                "    description: True DataFrame.\n",
                "    optional: false\n",
                "  rating_pred:\n",
                "    type: AnyDirectory\n",
                "    description: Predicted DataFrame.\n",
                "    optional: false\n",
                "  user_column:\n",
                "    type: String\n",
                "    description: Column name of user IDs.\n",
                "    default: UserId\n",
                "    optional: false\n",
                "  item_column:\n",
                "    type: String\n",
                "    description: Column name of item IDs.\n",
                "    default: MovieId\n",
                "    optional: false\n",
                "  rating_column:\n",
                "    type: String\n",
                "    description: Column name of ratings.\n",
                "    default: Rating\n",
                "    optional: false\n",
                "  prediction_column:\n",
                "    type: String\n",
                "    description: Column name of predictions.\n",
                "    default: prediction\n",
                "    optional: false\n",
                "  relevancy_method:\n",
                "    type: String\n",
                "    description: method for determining relevancy ['top_k', 'by_threshold'].\n",
                "    default: top_k\n",
                "    optional: false\n",
                "  top_k:\n",
                "    type: Integer\n",
                "    description: Number of top k items per user.\n",
                "    default: 10\n",
                "    optional: false\n",
                "  threshold:\n",
                "    type: Float\n",
                "    description: Threshold of top items per user.\n",
                "    default: 10.0\n",
                "    optional: false\n",
                "outputs:\n",
                "  score:\n",
                "    type: AnyDirectory\n",
                "    description: Precision at k (min=0, max=1).\n",
                "code:\n",
                "  ../../\n",
                "command: >-\n",
                "  python contrib/azureml_designer_modules/entries/precision_at_k_entry.py\n",
                "  --rating-true {inputs.rating_true} --rating-pred {inputs.rating_pred} --col-user\n",
                "  {inputs.user_column} --col-item {inputs.item_column} --col-rating {inputs.rating_column}\n",
                "  --col-prediction {inputs.prediction_column} --relevancy-method {inputs.relevancy_method}\n",
                "  --k {inputs.top_k} --threshold {inputs.threshold} --score-result {outputs.score}\n",
                "environment:\n",
                "  conda:\n",
                "    conda_dependencies_file: contrib/azureml_designer_modules/module_specs/sar_conda.yaml\n",
                "  os: Linux\n",
                "\n",
                "```\n",
                "\n",
                "In the yaml file we can see a number of sections. The heading defines attributes like name, version or description. In the section inputs, all inputs are defined. The two main dataframes have ports, which can be connected to other modules. The inputs without port appear in a canvas menu. The output is defined as a DataFrame as well. The last section, implementation, defines the conda environment, the associated python entry and the arguments to the python file.\n",
                "\n",
                "\n",
                "# Module Registration\n",
                "\n",
                "Once the code is implemented, we need to register it as an AzureML Designer custom module. The registration can be performed following these simple steps:"
            ]
        },
        {
            "source": [
                "## Register in studio UI\n",
                "\n",
                "You can directly register a custom module/component in the studio UI.\n",
                "\n",
                "Follow [this tutorial](https://aka.ms/component-tutorial1) to register the related components to your workspace."
            ],
            "cell_type": "markdown",
            "metadata": {}
        },
        {
            "source": [
                "## Register using CLI\n",
                "\n",
                "### CLI Installation\n",
                "\n",
                "The first step is to install [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) and Component CLI extension. Assuming that you have installed the Recommenders environment `reco_base` as explained in the [SETUP.md](../../SETUP.md).\n"
            ],
            "cell_type": "markdown",
            "metadata": {}
        },
        {
            "cell_type": "code",
            "execution_count": null,
            "metadata": {},
            "outputs": [],
            "source": [
                "conda activate reco_base\n",
                "pip install azure-cli"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": null,
            "metadata": {},
            "outputs": [],
            "source": [
                "# Login\n",
                "!az login -o none"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": null,
            "metadata": {},
            "outputs": [],
            "source": [
                "# Uninstall azure-cli-ml (the `az ml` commands)\n",
                "!az extension remove -n azure-cli-ml \n",
                "\n",
                "# Install remote version of azure-cli-ml (which includes `az ml component` commands)\n",
                "!az extension add --source https://azuremlsdktestpypi.blob.core.windows.net/wheels/modulesdkpreview/azure_cli_ml-0.1.0.29211468-py3-none-any.whl --pip-extra-index-urls https://azuremlsdktestpypi.azureedge.net/modulesdkpreview --yes --verbose"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": null,
            "metadata": {},
            "outputs": [],
            "source": [
                "!az account set -s \"Your subscription name\"\n",
                "!az ml folder attach -w \"Your workspace name\" -g \"Your resource group name\""
            ]
        },
        {
            "cell_type": "code",
            "execution_count": null,
            "metadata": {},
            "outputs": [],
            "source": [
                "import os\n",
                "import tempfile\n",
                "import shutil\n",
                "import subprocess"
            ]
        },
        {
            "cell_type": "code",
            "execution_count": null,
            "metadata": {},
            "outputs": [],
            "source": [
                "# Regsiter components with spec via Azure CLI\n",
                "root_path = os.path.abspath(os.path.join(os.getcwd(), \"../../\"))\n",
                "specs_folder = os.path.join(root_path, \"contrib/azureml_designer_modules/module_specs\")\n",
                "github_prefix = 'https://github.com/microsoft/recommenders/blob/main/recommenders/azureml/azureml_designer_modules/module_specs/'\n",
                "specs = os.listdir(specs_folder)\n",
                "for spec in specs:\n",
                "    spec_path = github_prefix + spec\n",
                "    print(f\"Start to register component spec: {spec} ...\")\n",
                "    subprocess.run(f\"az ml component create --file {spec_path}\", shell=True)\n",
                "    print(f\"Done.\")"
            ]
        },
        {
            "cell_type": "markdown",
            "metadata": {},
            "source": [
                "## Running Recommenders in AzureML Designer\n",
                "\n",
                "Once the modules are registered, they will appear in the canvas as the module `Recommenders`. There you will be able to create a pipeline like this:\n",
                "\n",
                "![img](https://raw.githubusercontent.com/Azure/AzureMachineLearningGallery/main/pipelines/sar-pipeline/sar-pipeline.png)\n",
                "\n",
                "Now, thanks to AzureML Designer, users can compute the latest state of the art algorithms in recommendation systems without writing a line of python code.\n",
                "\n",
                "## References\n",
                "\n",
                "1. [AzureML Designer documentation](https://docs.microsoft.com/en-us/azure/machine-learning/concept-designer)\n",
                "1. [Tutorial: Prediction of automobile price](https://aka.ms/designer-tutorial)\n",
                "1. [Tutorial: Register component to workspace](https://aka.ms/component-tutorial1)\n",
                "1. [Tutorial: Create a new component](https://aka.ms/component-tutorial2)"
            ]
        }
    ],
    "metadata": {
        "kernelspec": {
            "name": "python3",
            "language": "python",
            "display_name": "Python 3.6.8 64-bit ('test': conda)",
            "metadata": {
                "interpreter": {
                    "hash": "ad1389e27ccf93b6cb9b27912fdce5bd72b7d47f7c4b29627ffa9bc4b1e3e5d1"
                }
            }
        },
        "language_info": {
            "codemirror_mode": {
                "name": "ipython",
                "version": 3
            },
            "file_extension": ".py",
            "mimetype": "text/x-python",
            "name": "python",
            "nbconvert_exporter": "python",
            "pygments_lexer": "ipython3",
            "version": "3.6.8-final"
        }
    },
    "nbformat": 4,
    "nbformat_minor": 2
}